import { _decorator, Color, Component, game, instantiate, Label, Node, Prefab, profiler, Sprite, tween, Tween, UITransform, Vec2, Vec3 } from 'cc';
import { buildColors } from './ColorLib';
import { DropCtr } from './DropCtr';
import { PixelSprite } from './PixelSprite';
import { ShapeSprite } from './ShapeSprite';
const { ccclass, property } = _decorator;





@ccclass('Game')
export class Game extends Component {
    public static Instance: Game;

    isGameOver: boolean = false; //是否游戏结束
    isAnimating: boolean = false; //是否正在动画

    flags: number[][] = [];//流沙颜色标记,0代表透明，大于0是颜色
    colors: Color[][] = []; //流沙实际颜色 , 保存记录tetris显示数据
    flowCoord: number[][] = []; //流动的沙子坐标，优化下一次检测连通性

    private lastRewardIdx: number = 0;

    private lastDropIdx: number = 0; // 下落的形状缓存池索引
    private dropShapes: ShapeSprite[] = []; // 下落的形状缓存池


    @property(Node)
    drops!: Node;

    @property(Label)
    scores!: Label;

    @property(Node)
    rewards!: Node;


    @property(PixelSprite)
    tetris!: PixelSprite;

    @property(Node)
    deathLine!: Node;

    @property(Sprite)
    dropShadow!: Sprite;

    @property(Prefab)
    shapePrefab!: Prefab;


    @property([ShapeSprite])
    shapes: ShapeSprite[] = [];

    @property({ type: [Color], visible() { return false; } })
    private _colorMaps: Color[] = [
        new Color(245, 205, 48, 255),
        new Color(75, 151, 75, 255),
        new Color(196, 40, 28, 255),
        new Color(13, 105, 172, 255)];

    @property([Color])
    get colorMaps() {
        return this._colorMaps
    }
    set colorMaps(colors: Color[]) {
        this._colorMaps = colors;
        buildColors(colors, this.randColors);
    }


    private static _instance: Game;
    public static get instance(): Game {
        return Game._instance;
    }

    protected onLoad(): void {
        Game.Instance = this;

        this.initGame();
        // 尝试加载保存的游戏状态（如果存在）
        this.loadGameState();
        Game._instance = this;
        profiler.showStats(); //性能测试面板
        //game.frameRate = 120;
    }

    protected onDestroy(): void {
        // 在节点销毁时保存当前游戏状态
        try {
            this.saveGameState();
        } catch (e) {
            console.warn('saveGameState failed in onDestroy', e);
        }
        this.unscheduleAllCallbacks();
    }


    level: number = 0;
    currScore: number = 0;
    totalScore: number = 0;
    matchCount: number = 0; //消除方块个数
    matchTimes: number = 0; //连续消除次数
    heightLimit: number = 0; //高度限制线格数

    //每一个主色的随机花色数组
    public randColors: Color[][] = [];

    // 访问标记数组，辅助连通性检测
    private visited: boolean[][] = [];



    private async initGame() {


        this.updateHeightLimit();

        buildColors(this.colorMaps, this.randColors);

        //显示下落方块缓存池
        for (let i = 0; i < 5; i++) {
            let node = instantiate(this.shapePrefab);
            this.drops.addChild(node);

            let s = node.getComponent(ShapeSprite);
            this.dropShapes.push(s);
            s.display = false;
        }

        //分数缓存池
        let rewards = this.rewards.getComponentsInChildren(Label);
        for (let i = 0; i < rewards.length; i++) {
            let label = rewards[i];
            if (label) {
                label.enabled = false;
            }
        }

        //俄罗斯面板状态和数据
        let w = this.tetris.texWidth;
        let h = this.tetris.texHeight;
        for (let i = 0; i < h; i++) {
            let visited = this.visited[i] = [];
            let colors = this.colors[i] = [];
            let flags = this.flags[i] = [];

            for (let j = 0; j < w; j++) {
                visited[j] = new Array(w).fill(false);
                colors[j] = new Color(0, 0, 0, 0);
                flags[j] = 0;
            }
        }


        await new Promise(res => this.scheduleOnce(res, 0.2));

        this.loadGameState();

        this.schedule(this.autoSave, 1);
    }

    autoSave() {
        this.saveGameState();
    }


    private resetGame() {
        if (this.isAnimating) return;

        let drops = this.drops.getComponentsInChildren(ShapeSprite);
        //显示下落方块缓存池
        for (let i = 0; i < drops.length; i++) {
            let drop = drops[i];
            if (drop) {
                drop.display = false;
            }
        }


        let rewards = this.rewards.getComponentsInChildren(Label);
        //显示下落方块缓存池
        for (let i = 0; i < rewards.length; i++) {
            let label = rewards[i];
            if (label) {
                label.enabled = false;
            }
        }

        //俄罗斯面板状态和数据
        let w = this.tetris.texWidth;
        let h = this.tetris.texHeight;
        for (let i = 0; i < h; i++) {
            let colors = this.colors[i];// = [];
            let flags = this.flags[i];// = [];
            for (let j = 0; j < w; j++) {
                colors[j].set(0, 0, 0, 0);
                flags[j] = 0;
            }
        }


        this.tetris.updateDatas(this.colors, 0, 0, true, true);
        this.flowCoord.length = 0;
        this.lastRewardIdx = 0;
        this.lastDropIdx = 0;

        //分数板清0
        this.scores.string = "0";
        this.totalScore = 0;
        this.currScore = 0;


    }


    /**
     * 保存当前游戏状态到 localStorage（JSON）。
     * 会保存 flags、colors、分数、flowCoord 等必要恢复数据。
     */
    saveGameState(): void {
        try {
            if (typeof localStorage === 'undefined') return;
            const key = 'sand_tetris_save_v1';
            const w = this.tetris.texWidth;
            const h = this.tetris.texHeight;

            const colorsSer: { r: number; g: number; b: number; a: number; }[][] = [];
            for (let y = 0; y < h; y++) {
                const row: { r: number; g: number; b: number; a: number; }[] = [];
                for (let x = 0; x < w; x++) {
                    const c = this.colors[y][x];
                    row.push({ r: c.r, g: c.g, b: c.b, a: c.a });
                }
                colorsSer.push(row);
            }

            // 序列化右侧操作区的 shapes（通常为3个）
            const shapesSer: any[] = [];
            if (this.shapes && this.shapes.length) {
                for (let s of this.shapes) {
                    if (!s) {
                        shapesSer.push(null);
                        continue;
                    }
                    const flagCopy = s.flags ? s.flags.map(r => r.slice()) : [];
                    const colorCopy = (s.colors || []).map(r => r.map((c: Color) => ({ r: c.r, g: c.g, b: c.b, a: c.a })));
                    shapesSer.push({
                        shapeID: s.shapeID,
                        color0Idx: s.color0Idx,
                        color1Idx: s.color1Idx,
                        score: s.score,
                        shapeRow: s.shapeRow,
                        shapeCol: s.shapeCol,
                        flags: flagCopy,
                        colors: colorCopy
                    });
                }
            }

            const state = {
                version: 1,
                isGameOver: this.isGameOver,
                currScore: this.currScore,
                totalScore: this.totalScore,
                matchTimes: this.matchTimes,
                heightLimit: this.heightLimit,
                flags: this.flags,
                colors: colorsSer,
                flowCoord: this.flowCoord,
                shapes: shapesSer,
                timestamp: Date.now(),
            };

            localStorage.setItem(key, JSON.stringify(state));
            // console.log('Game saved to', key);
        } catch (e) {
            console.warn('saveGameState error', e);
        }
    }

    /**
     * 从 localStorage 加载保存的游戏状态并恢复到当前面板。
     * 返回 true 表示成功恢复，false 表示没有存档或恢复失败。
     */
    loadGameState(): boolean {
        try {
            if (typeof localStorage === 'undefined') return false;
            const key = 'sand_tetris_save_v1';
            const json = localStorage.getItem(key);
            if (!json) return false;
            const state = JSON.parse(json);

            const w = this.tetris.texWidth;
            const h = this.tetris.texHeight;

            if (!state.flags || !Array.isArray(state.flags) || state.flags.length !== h) {
                // 存档尺寸不匹配，忽略
                console.warn('Saved game size mismatch, ignore load');
                return false;
            }

            this.isGameOver = !!state.isGameOver;
            this.currScore = state.currScore || 0;
            this.totalScore = state.totalScore || 0;
            this.matchTimes = state.matchTimes || 0;
            this.heightLimit = state.heightLimit || this.heightLimit;

            // 恢复 flags/colors
            for (let y = 0; y < h; y++) {
                for (let x = 0; x < w; x++) {
                    this.flags[y][x] = state.flags[y][x] || 0;
                    const cd = (state.colors && state.colors[y] && state.colors[y][x]) ? state.colors[y][x] : { r: 0, g: 0, b: 0, a: 0 };
                    this.colors[y][x].set(cd.r, cd.g, cd.b, cd.a);
                }
            }

            this.flowCoord = Array.isArray(state.flowCoord) ? state.flowCoord.slice() : [];

            // 恢复右侧操作区 shapes（如果有）
            try {
                if (state.shapes && Array.isArray(state.shapes) && this.shapes && this.shapes.length) {
                    for (let i = 0; i < this.shapes.length; i++) {
                        const ser = state.shapes[i];
                        const target = this.shapes[i];
                        if (!target) continue;
                        if (!ser) {
                            // 隐藏无效的 shape
                            target.display = false;
                            continue;
                        }

                        // 恢复基础属性
                        target.shapeID = ser.shapeID || 0;
                        target.color0Idx = ser.color0Idx || 0;
                        target.color1Idx = ser.color1Idx || 0;
                        target.score = ser.score || 0;

                        // 恢复逻辑行列，这对计算显示位置很重要
                        const row = ser.shapeRow || target.shapeRow || 1;
                        const col = ser.shapeCol || target.shapeCol || 1;
                        target.shapeRow = row;
                        target.shapeCol = col;

                        // 计算 shapeCenter 所需的 w,h（内部 pixelSize = 6）
                        const pixelSize = 6;
                        const w = Math.round(target.texWidth / pixelSize);
                        const h = Math.round(target.texHeight / pixelSize);
                        try { target.shapeCenter(row, col, w, h); } catch (e) { }

                        // 恢复 flags（深拷贝）
                        target.flags = [];
                        if (ser.flags && Array.isArray(ser.flags)) {
                            for (let y = 0; y < ser.flags.length; y++) {
                                target.flags[y] = (ser.flags[y] && Array.isArray(ser.flags[y])) ? ser.flags[y].slice() : [];
                            }
                        }

                        // 恢复 colors
                        target.colors = [];
                        if (ser.colors && Array.isArray(ser.colors)) {
                            for (let y = 0; y < ser.colors.length; y++) {
                                target.colors[y] = [];
                                for (let x = 0; x < ser.colors[y].length; x++) {
                                    const cd = ser.colors[y][x] || { r: 0, g: 0, b: 0, a: 0 };
                                    target.colors[y][x] = new Color(cd.r, cd.g, cd.b, cd.a);
                                }
                            }
                        }

                        // 更新显示
                        try { target.updateDatas(target.colors, 0, 0, true, true); } catch (e) { }
                    }
                }
            } catch (e) {
                console.warn('restore shapes failed', e);
            }

            // 更新显示
            this.tetris.updateDatas(this.colors, 0, 0, true, true);
            if (this.scores) this.scores.string = '' + this.totalScore;

            // console.log('Loaded saved game');
            return true;
        } catch (e) {
            console.warn('loadGameState error', e);
            return false;
        }
    }

    clearSavedState(): void {
        try {
            if (typeof localStorage === 'undefined') return;
            localStorage.removeItem('sand_tetris_save_v1');
        } catch (e) {
            console.warn('clearSavedState error', e);
        }
    }

    private stopGame() {

    }


    private overGame() {
        console.log("overGame");
    }

    /**
     * 生成下全部一下组形状
     */
    private nextShapes() {
        if (this.isAnimating) return;
        let shapes = this.shapes;
        for (let i = 0; i < shapes.length; i++) {
            let shape = shapes[i];
            if (shape) {
                shape.nextShape();
            }
        }
    }

    private scoreAddAnimation() {

        //总分增加动画
        if (this.currScore != this.totalScore) {

            let dist = this.totalScore - this.currScore;
            let step = ~~(dist * 0.04) + 1;
            this.currScore += step;

            if (this.currScore > this.totalScore) {
                this.currScore = this.totalScore;
            }

            this.scores.string = "" + this.currScore;
        }
    }



    private scoreFlyAnimation(currLabel: Label, combo: number) {


        Tween.stopAllByTarget(currLabel.node);
        // 基础颜色副本，初始不透明
        const base = new Color(currLabel.color);
        let toPos = new Vec3(currLabel.node.position);

        if (combo > 0) {

            //消除连击,得分
            currLabel.node.setScale(1.5, 1.5);
            // combo不为0时，飞到deathline位置
            if (this.deathLine) {
                const deathLinePos = this.deathLine.position;
                toPos.y = deathLinePos.y + 50;
            }

            // 先完成位置飞行，飞行完成后再进行透明处理
            tween(currLabel.node).delay(0.1).
                to(0.5, { position: toPos }, { easing: 'quartOut' }).call(() => {
                    // 位置飞行完成后，开始透明处理
                    const alphaHolder = { a: 255 };
                    tween(alphaHolder).to(0.6, { a: 0 }, {
                        onUpdate: () => {
                            currLabel.color = new Color(base.r, base.g, base.b, alphaHolder.a);
                        }
                    }).call(() => { currLabel.enabled = false; }).start();
                }).start();

        } else {
            //普通下放得分
            toPos.y += 120;

            let liftime = 0.6;

            currLabel.node.setScale(1, 1);

            tween(currLabel.node).to(liftime, { position: toPos }, { easing: 'cubicOut' }).start();

            // 同步淡出（alpha 255->0）并在结束时隐藏
            const alphaHolder = { a: 255 };
            tween(alphaHolder).to(liftime, { a: 0 }, {
                onUpdate: () => {
                    currLabel.color = new Color(base.r, base.g, base.b, alphaHolder.a);
                }
            }).call(() => { currLabel.enabled = false; }).start();
        }


    }


    addScores(count: number, combo: number, x: number, y: number, colorIdx: number) {

        console.log("addScores", count, combo, x, y, colorIdx);

        let lables = this.rewards.getComponentsInChildren(Label);
        let length = lables.length;

        this.totalScore += count; //统计分数

        if (length) {

            let idx = (this.lastRewardIdx++) % length;
            let currLabel = lables[idx];

            //激活显示
            currLabel.enabled = true;
            currLabel.string = "" + count;
            currLabel.color = this.randColors[colorIdx][0];


            let w = this.tetris.texWidth;
            let h = this.tetris.texHeight;
            let t = this.tetris.getComponent(UITransform);

            let offx = t.width * x / w - t.width / 2;
            let offy = t.height / 2 - t.height * y / h;

            currLabel.node.setPosition(offx, offy);

            this.scoreFlyAnimation(currLabel, combo);

        }

    }

    /**
     * 游戏结束动画
     */
    private overAnimation() {

        // 当前处理的层数
        let currentLayer = 0;
        let w = this.tetris.texWidth;
        let h = this.tetris.texHeight;

        this.isAnimating = true;
        const updateFunc = () => {
            if (currentLayer >= h) {
                // 所有层都处理完毕
                this.unschedule(updateFunc);
                this.isAnimating = false;
                this.overGame();
                return;
            }

            // 计算本次要置灰的层数（加速增长）
            let layersToProcess = Math.floor(1 + currentLayer * 0.1); // 从1层开始，每次增加0.5层
            layersToProcess = Math.min(layersToProcess, h - currentLayer); // 不超过剩余层数

            // 处理多层层
            for (let layer = 0; layer < layersToProcess; layer++) {
                let y = currentLayer + layer;
                for (let x = 0; x < w; x++) {
                    if (this.flags[y][x] > 0) {
                        // 设置当前层为灰色
                        this.colors[y][x].set(128, 128, 128, 255);
                    }
                }
            }

            // 更新 tetris 显示
            this.tetris.updateDatas(this.colors, 0, 0, false, true);

            // 移动到下一批次的起始层
            currentLayer += layersToProcess;
        };

        this.schedule(updateFunc);
    }


    /**
     * 检查下放方块是否可以下放
     * @param shape 下放方块
     * @param col 下放方块的列
     * @param row 下放方块的行
     * @returns 是否可以下放
     */
    private checkDrop(shape: ShapeSprite, col: number, row: number) {
        if (this.isGameOver) return false;

        // 边界保护：检查起始位置是否有效
        if (row < 0 || col < 0) {
            return false;
        }

        for (let i = 0; i < shape.gridsW; i++) {
            for (let j = 0; j < shape.gridsH; j++) {
                // 边界保护：检查结束位置是否超出范围
                if (row + j >= this.tetris.texHeight
                    || col + i >= this.tetris.texWidth) {
                    return false;
                }

                //像素相交，不能下放，或者是游戏结束
                if (shape.flags[j][i] > 0 && this.flags[row + j][col + i] > 0) {
                    if (row == 0) {
                        this.isGameOver = true;
                    }
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * 将形状绘制到主面板
     * @param shape 形状
     * @param grid 形状的格子坐标
     */
    private drawAtTetris(shape: ShapeSprite, grid: Vec2) {
        //加入flag
        for (let i = 0; i < shape.gridsH; i++) {
            for (let j = 0; j < shape.gridsW; j++) {
                let flag = shape.flags[i][j];
                let color = shape.colors[i][j];
                if (flag > 0) {
                    this.flags[grid.y + i][grid.x + j] = flag;
                    this.colors[grid.y + i][grid.x + j].set(color);
                }
            }
        }

        //强制覆盖，延后刷新
        this.tetris.updateDatas(shape.colors, grid.x, grid.y, false, true);
    }

    /**
     * 添加下放方块到主面板的下落队列（缓存池复用）
     * 1. 采用循环缓存池（dropShapes），避免频繁创建/销毁节点。
     * 2. 复制传入的 shape 数据到缓存池中的 ShapeSprite。
     * 3. 调用 DropCtr 组件的 reset 方法，设置初始格子坐标。
     * 4. 用于新方块下落动画和逻辑的统一入口。
     * @param shape 传入的 ShapeSprite（形状数据）
     * @param grid  下落起始格子坐标（Vec2）
     */
    drop(shape: ShapeSprite, grid: Vec2) {

        let canDrop = this.checkDrop(shape, grid.x, grid.y);

        //游戏结束
        if (this.isGameOver) {
            //强制shape覆盖tetris显示
            this.drawAtTetris(shape, grid);
            //显示游戏结束动画
            this.overAnimation();
            return false;
        }

        //可以下放
        if (canDrop) {
            //下放方块循环缓存池
            let length = this.dropShapes.length;
            let idx = (this.lastDropIdx++) % length;
            let currShape = this.dropShapes[idx];

            //复制下放方块数据，并启动
            if (currShape) {
                let drop = currShape.getComponent(DropCtr);
                currShape.copyShape(shape);
                drop.reset(grid.x, grid.y);
            }
        }

        return canDrop;
    }


    /**
     * 检测并消除横向贯穿的流沙连通块（只检测活跃流沙坐标 flowCoord）
     * 1. 对 flowCoord 中每个活跃流沙像素，广度优先搜索（BFS）其 flag 连通块。
     * 2. 记录连通块的最小/最大横坐标（minX/maxX），判断是否横跨左右边界。
     * 3. 只要有一个连通块横跨左右边界（minX==0 且 maxX==w-1），则将该连通块加入消除队列。
     * 4. 对所有待消除像素，flag 置 0，颜色设透明，实现消除。
     * 5. 返回是否有消除发生。
     * 优点：只检测活跃流沙像素，提升效率，适合像素流沙玩法的横满即消逻辑。
     */
    private matching() {

        if (this.isAnimating) return;

        let w = this.tetris.texWidth;
        let h = this.tetris.texHeight;


        let visited = this.visited;

        // 标记访问过的像素，防止重复检测
        for (let i = 0; i < h; i++) visited[i].fill(false);

        //八方向扩展，如需要增加难度，可仅留前四个方向
        let directions = [[0, 1], [0, -1], [1, 0], [-1, 0], [1, 1], [1, -1], [-1, -1], [-1, 1]];


        // 广度优先搜索flag连通块，且连通到左右边缘的才消除
        for (let i = 0; this.flowCoord.length > 0; i++) {
            let coord = this.flowCoord.pop();
            if (!coord) break;
            let x = coord[1];
            let y = coord[0];
            if (this.flags[y][x] > 0 && !visited[y][x]) {
                let flag = this.flags[y][x];
                let queue: [number, number][] = [[y, x]];
                let group: [number, number][] = [];
                let minX = x, maxX = x;
                visited[y][x] = true;
                let addx = 0, addy = 0;
                this.matchCount = 0;
                while (queue.length) {
                    let [cy, cx] = queue.shift();
                    group.push([cy, cx]);
                    if (cx < minX) minX = cx;
                    if (cx > maxX) maxX = cx;
                    this.matchCount++;
                    // 四方向扩展
                    for (let [dy, dx] of directions) {
                        let ny = cy + dy, nx = cx + dx;
                        if (ny >= 0 && ny < h && nx >= 0 && nx < w && this.flags[ny][nx] === flag && !visited[ny][nx]) {
                            queue.push([ny, nx]);
                            visited[ny][nx] = true;
                            addx += nx;
                            addy += ny;
                        }
                    }
                }
                // 连通到左右边缘
                if (minX === 0 && maxX === w - 1) {

                    this.matchTimes++;
                    this.scheduleOnce(() => {
                        let x = ~~(addx / this.matchCount);
                        let y = ~~(addy / this.matchCount);
                        this.addScores(this.matchCount, this.matchTimes, x, y, flag - 1);
                    }, 0.0);

                    this.matchAnaimtion(group);

                    break;
                }
            }
        }

    }

    /**
     * 消除动画：先整体闪白，再彻底清理像素
     * @param oneClear 待消除像素的[y, x]坐标数组
     */
    private matchAnaimtion(oneClear: [number, number][]) {
        if (oneClear.length <= 0) return;

        let times = 6;
        this.isAnimating = true;
        const updateFunc = () => {
            times--;
            if (times <= 0) {
                for (let [y, x] of oneClear) {
                    this.colors[y][x].set(0, 0, 0, 0);
                    this.flags[y][x] = 0;
                }
                this.tetris.updateDatas(this.colors, 0, 0, false, true);
                this.unschedule(updateFunc);
                this.isAnimating = false;
                return;
            }
            //消除闪白动画
            for (let [y, x] of oneClear) {
                this.colors[y][x].set(255, 255, 255, times % 2 ? 255 : 128);
            }
            this.tetris.updateDatas(this.colors, 0, 0, false, true);
        };
        this.schedule(updateFunc, 0.1);

    }


    /**
     * 流沙扩散主逻辑：推动所有可流动像素下落或扩散
     * 1. 从下往上遍历所有行，每行交错左右遍历，提升扩散对称性和自然感。
     * 2. 对每个可流动像素，优先尝试向下移动（下方为空）。
     * 3. 若不能下落，则尝试左右扩散（下方左右为空，交错顺序）。
     * 4. 每次移动后，将新位置加入 flowCoord，供下一帧消除检测。
     * 5. moved 数组防止本轮重复移动同一像素。
     * 返回值：是否有像素发生移动。
     *
     * 该方法保证所有悬空流沙都能自然下落和扩散，且只记录活跃像素，提升性能。
     */
    private flowSand() {
        if (this.isAnimating) return;

        let w = this.tetris.texWidth;
        let h = this.tetris.texHeight;
        let changed = false;



        let moved = this.visited; // 标记本轮已移动，防止多次扩散

        let directions = [[0, -1, 1], [0, 1, -1]]; // 下，左下，右下

        for (let i = 0; i < h; i++) moved[i].fill(false);

        for (let y = h - 2; y >= 0; y--) {
            // 交错起始方向，提升对称性
            let xStart = y % 2 === 0 ? 0 : w - 1;
            let xEnd = y % 2 === 0 ? w : -1;
            let xStep = y % 2 === 0 ? 1 : -1;
            for (let x = xStart; x !== xEnd; x += xStep) {
                if (this.flags[y][x] > 0 && !moved[y][x]) {
                    //优先向下,左右扩散顺序交错，提升对称性
                    let dirs = directions[y % 2]; //y % 2 === 0 ? [0,-1, 1] : [0 , 1 , -1];
                    for (let d of dirs) {
                        let nx = x + d;
                        if (nx >= 0 && nx < w && this.flags[y + 1][nx] === 0 && !moved[y + 1][nx]) {
                            this.flags[y + 1][nx] = this.flags[y][x];
                            let t = this.colors[y + 1][nx];
                            this.colors[y + 1][nx] = this.colors[y][x];
                            this.colors[y][x] = t;
                            this.flags[y][x] = 0;
                            t.set(0, 0, 0, 0);

                            changed = true;
                            moved[y + 1][nx] = true;
                            this.flowCoord.push([y + 1, nx]);
                            break;
                        }
                    }
                }
            }
        }


        if (changed) {
            //有流动，更新显示
            this.tetris.updateDatas(this.colors, 0, 0, false, true);
        } else {
            //没流动，检测死亡线高度,是否触碰
            this.checkDeathTouch();
        }

    }

    /**
     * 仅检测 heightLimit 这一行是否存在像素，若有则触碰死亡线并结束
     */
    private checkDeathTouch() {

        let w = this.tetris.texWidth;
        let limit = this.heightLimit;
        for (let x = 0; x < w; x++) {
            if (this.flags[limit][x] > 0) {
                this.isGameOver = true;
                this.overAnimation();
                break;
            }
        }
    }

    /**
     * 根据 deathLine 的世界坐标，换算到 tetris 本地坐标后映射到网格行。
     * 约定：gridY=0 为最顶行，gridY=h-1 为最底行。
     */
    private updateHeightLimit() {
        if (!this.deathLine || !this.tetris) return;
        const t = this.tetris.getComponent(UITransform);
        if (!t) return;

        // 转到 tetris 本地（以锚点为原点）
        const worldPos = this.deathLine.worldPosition;
        const local = this.deathLine.position; //t.convertToNodeSpaceAR(worldPos);

        const texH = this.tetris.texHeight;
        const uiH = t.height;

        // y 像素 -> 网格行（顶部为 0）
        let gridY = Math.floor((uiH / 2 - local.y) * texH / uiH);
        if (gridY >= texH) gridY = texH - 1;
        if (gridY < 0) gridY = 0;

        this.heightLimit = gridY;
    }

    private processing(dt: number) {
        if (this.isGameOver) return;

        //消除检测和动画
        this.matching();

        let step = 1;//3以内
        //最大模拟步数，提高沙流速度
        while (--step >= 0) {
            //流沙检测和动画
            this.flowSand();

            //没有流动，停止状态
            if (this.flowCoord.length == 0) {
                //重置 combo 次数
                this.matchTimes = 0;
                break;
            }
        }

        this.scoreAddAnimation();

    }

    protected update(dt: number): void {
        this.processing(dt);
    }
}

window["Game"] = Game;